{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Part 8 bis - プロトコル入門\n",
    "\n",
    "### コンテクスト \n",
    "\n",
    "前回のチュートリアルではPlanについてご紹介しました。今回はProtocolという概念についてです。Protocolは複数のPlanを組み合わせ、リモートワーカーの環境下にデプロイすることができます。デプロイ後は一回のコミュニケーションで実行することができます。\n",
    "\n",
    "Protocolは複雑な演算処理を複数のワーカーに分散させることができる外観的なオブジェクトです。Protocolの重要な特徴は、ワーカー間で送られたり、検索されたりして、最終的に特定のワーカーにデプロイできる事です。そのため、ユーザーはProtocolをデザインすることで、ワーカーはそれをダウンロード、適用して、プログラムを実行する、なんて事が実現できるのです。\n",
    "\n",
    "では、見てみましょう。\n",
    "\n",
    "Authors:\n",
    "- Théo Ryffel - Twitter [@theoryffel](https://twitter.com/theoryffel) - GitHub: [@LaRiffle](https://github.com/LaRiffle)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. 作成とデプロイ"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Protocolは`worker`と`plan`の組みによって作成されます。`worker`は実際のワーカーでもワーカーIDでも構いません。`plan`はオブジェクトでもポインタでも構いません。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch as th\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(th)\n",
    "\n",
    "# IMPORTANT: ローカルワーカーはクライアントワーカーにはなれません\n",
    "hook.local_worker.is_client_worker = False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Planを3つ定義して、Protocolとして纏めてみましょう。機能は全て1を足すオペレーションです。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@sy.func2plan(args_shape=[(1,)])\n",
    "def inc1(x):\n",
    "    return x + 1\n",
    "\n",
    "@sy.func2plan(args_shape=[(1,)])\n",
    "def inc2(x):\n",
    "    return x + 1\n",
    "\n",
    "@sy.func2plan(args_shape=[(1,)])\n",
    "def inc3(x):\n",
    "    return x + 1\n",
    "\n",
    "protocol = sy.Protocol([(\"worker1\", inc1), (\"worker2\", inc2), (\"worker3\", inc3)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "次にProtocolをワーカーにくっつける必要があります。これは`.deploy(*workers)`コマンドで実行できます。では、ワーカーを作成しましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob = sy.VirtualWorker(hook, id=\"bob\")\n",
    "alice = sy.VirtualWorker(hook, id=\"alice\")\n",
    "charlie = sy.VirtualWorker(hook, id=\"charlie\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "workers = alice, bob, charlie\n",
    "\n",
    "protocol.deploy(*workers)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "各Planはそれぞれ関連づけられたワーカーの元に既に送られています。既にデプロイされているんです。\n",
    "\n",
    "実は2つのフェーズから成り立っています。第一フェーズはワーカーとPlanの関連付けです。この段階ではワーカーの名前の文字列と関連づけられているだけです。第二フェーズでは、実際に各Planが各ワーカーの元へ送信されます。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Protocolの実行\n",
    "\n",
    "Protocolを実行するというのは、全てのPlanを順番に実行する事と同義です。そのためにやるべき事は、最初のPlanが配置されている場所に入力データを送り込む事だけです。すると最初のPlanは実行され、その結果が次のPlanの入力データとなります。全てのPlanが順次実行され、最後のPlanが完了するとポインタが返されます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = th.tensor([1.0])\n",
    "ptr = protocol.run(x)\n",
    "ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ptr.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "入力された1.0は3つのPlanを通過して3度1がたされ、4になって戻ってきています。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "実は、引数にポインタを使ってPlanをリモート実行することも可能です。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "james = sy.VirtualWorker(hook, id=\"james\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "protocol.send(james)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = th.tensor([1.0]).send(james)\n",
    "ptr = protocol.run(x)\n",
    "ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "見ての通り、結果として返されるポインタはJamesがもっていますね。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ptr = ptr.get()\n",
    "ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ptr = ptr.get()\n",
    "ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Protocolの検索\n",
    "\n",
    "実運用では、外部からProtocolをダウンロードしてきて、あなたのリモートワーカーにデプロイし、あなたのデータに対して実行したいと思うかもしれません。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "それでは**まだデプロイされていない**Protocolを初期化して、リモートワーカーへ送ってみましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "protocol = sy.Protocol([(\"worker1\", inc1), (\"worker2\", inc2), (\"worker3\", inc3)])\n",
    "protocol.tag('my_protocol')\n",
    "protocol.send(james)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "me = sy.hook.local_worker # ローカルワーカーをmeとして取得しておきます"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ここでProtocolを検索してみましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "responses = me.request_search(['my_protocol'], location=james)\n",
    "responses"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "今、Protocolのポインタに対してアクセスできます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ptr_protocol = responses[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "他のポインタと同じように、実態を受け取る事も出来ます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "protocol_back = ptr_protocol.get()\n",
    "protocol_back"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "後は先ほど学んだ手順と同様です。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "protocol_back.deploy(alice, bob, charlie)\n",
    "\n",
    "x = th.tensor([1.0])\n",
    "ptr = protocol_back.run(x)\n",
    "ptr.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Protocol関連のより実践的な例はこれから追加される予定ですが、Protocolが持つ可能性については十分に感じて頂けたと思います。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### PySyftのGitHubレポジトリにスターをつける\n",
    "\n",
    "一番簡単に貢献できる方法はこのGitHubのレポジトリにスターを付けていただくことです。スターが増えると露出が増え、より多くのデベロッパーにこのクールな技術の事を知って貰えます。\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Slackに入る\n",
    "\n",
    "最新の開発状況のトラッキングする一番良い方法はSlackに入ることです。\n",
    "下記フォームから入る事ができます。\n",
    "[http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### コードプロジェクトに参加する\n",
    "\n",
    "コミュニティに貢献する一番良い方法はソースコードのコントリビューターになることです。PySyftのGitHubへアクセスしてIssueのページを開き、\"Projects\"で検索してみてください。参加し得るプロジェクトの状況を把握することができます。また、\"good first issue\"とマークされているIssueを探す事でミニプロジェクトを探すこともできます。\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### 寄付\n",
    "\n",
    "もし、ソースコードで貢献できるほどの時間は取れないけど、是非何かサポートしたいという場合は、寄付をしていただくことも可能です。寄附金の全ては、ハッカソンやミートアップの開催といった、コミュニティ運営経費として利用されます。\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
